﻿<!DOCTYPE html>

<html lang="en" xmlns="http://www.w3.org/1999/xhtml">
<head>
    <meta charset="utf-8" />
    <title>WebSocket MSE Fmp4 demo</title>
</head>
<body>
    <h1>MSE FMp4 Demo</h1>
    <video id="stream_live" width="640" height="480" controls="false" autoplay="true"
           muted="muted"
           preload="auto">
        浏览器不支持
    </video>
    <ul id="messagesList"></ul>
    <script>
        //var mimeCodec = 'video/mp4;codecs="avc1.4D0014, mp4a.40.2"';
        // *** USER PARAMETERS ***
        var verbose = true;
        // var verbose = true; // enable for saturating the console ..
        var buffering_sec = 1; // use some reasonable value
        var buffering_sec_seek = buffering_sec * 0.9;
        // ..seek the stream if it's this much away or
        // from the last available timestamp
        var buffering_sec_seek_distance = buffering_sec * 0.5;
        // .. jump to this distance from the last avail. timestamp
        // *** INTERNAL PARAMETERS ***
        // set mimetype and codec
        var mimeType = "video/mp4";
        var codecs = "avc1.4D0014"; // https://wiki.whatwg.org/wiki/Video_type_parameters
        // if your stream has audio, remember to include it in these definitions.. otherwise your mse goes sour
        var codecPars = mimeType + ';codecs="' + codecs + '"';
        var stream_started = false; // is the source_buffer updateend callback active nor not
        // create media source instance
        var ms = new MediaSource();
        // queue for incoming media packets
        var queue = [];
        var stream_live; // the HTMLMediaElement (i.e. <video> element)
        var ws; // websocket
        var seeked = false; // have have seeked manually once ..
        var cc = 0;
        var source_buffer; // source_buffer instance
        var pass = 0;
        // *** MP4 Box manipulation functions ***
        // taken from here: https://stackoverflow.com/questions/54186634/sending-periodic-metadata-in-fragmented-live-mp4-stream/
        function toInt(arr, index) { // From bytes to big-endian 32-bit integer.  Input: Uint8Array, index
            var dv = new DataView(arr.buffer, 0);
            return dv.getInt32(index, false); // big endian
        }
        function toString(arr, fr, to) { // From bytes to string.  Input: Uint8Array, start index, stop index.
            // https://developers.google.com/web/updates/2012/06/How-to-convert-ArrayBuffer-to-and-from-String
            return String.fromCharCode.apply(null, arr.slice(fr, to));
        }
        function getBox(arr, i) { // input Uint8Array, start index
            return [toInt(arr, i), toString(arr, i + 4, i + 8)]
        }
        function getSubBox(arr, box_name) { // input Uint8Array, box name
            var i = 0;
            res = getBox(arr, i);
            main_length = res[0]; name = res[1]; // this boxes length and name
            i = i + 8;
            var sub_box = null;
            while (i < main_length) {
                res = getBox(arr, i);
                l = res[0]; name = res[1];

                if (box_name == name) {
                    sub_box = arr.slice(i, i + l)
                }
                i = i + l;
            }
            return sub_box;
        }
        function hasFirstSampleFlag(arr) { // input Uint8Array
            // [moof [mfhd] [traf [tfhd] [tfdt] [trun]]]
            var traf = getSubBox(arr, "traf");
            if (traf == null) { return false; }
            var trun = getSubBox(traf, "trun");
            if (trun == null) { return false; }
            // ISO/IEC 14496-12:2012(E) .. pages 5 and 57
            // bytes: (size 4), (name 4), (version 1 + tr_flags 3)
            var flags = trun.slice(10, 13); // console.log(flags);
            f = flags[1] & 4; // console.log(f);
            return f == 4;
        }
        // consider these callbacks:
        // - putPacket : called when websocket receives data
        // - loadPacket : called when source_buffer is ready for more data
        // Both operate on a common fifo
        function putPacket(arr) {
            // receives ArrayBuffer.  Called when websocket gets more data
            // first packet ever to arrive: write directly to source_buffer
            // source_buffer ready to accept: write directly to source_buffer
            // otherwise insert it to queue
            var memview = new Uint8Array(arr);
            if (verbose) { console.log("got", arr.byteLength, "bytes.  Values=", memview[0], memview[1], memview[2], memview[3], memview[4]); }
            res = getBox(memview, 0);
            main_length = res[0]; name = res[1]; // this boxes length and name
            // if ((name == "ftyp") && (pass == 0)) {
            //    pass = pass + 1;
            //    console.log("got ftyp");
            // }
            // else if ((name == "moov") && (pass == 1)) {
            //    pass = pass + 1;
            //    console.log("got moov");
            // }
            // else if ((name == "moof") && (pass == 2)) {
            //    if (hasFirstSampleFlag(memview)) {
            //        pass = pass + 1;
            //        console.log("got that special moof");
            //    }
            //    else {
            //        return;
            //    }
            // }
            // else if (pass < 3) {
            //    return;
            // }
            // keep the latency to minimum
            // let latest = stream_live.duration;
            // if ((stream_live.duration >= buffering_sec) && ((latest - stream_live.currentTime) > buffering_sec_seek)) {
            //     console.log("seek from ", stream_live.currentTime, " to ", latest);
            //     df = (stream_live.duration - stream_live.currentTime); // this much away from the last available frame
            //     if ((df > buffering_sec_seek)) {
            //         seek_to = stream_live.duration - buffering_sec_seek_distance;
            //         stream_live.currentTime = seek_to;
            //     }
            // }
            if (!source_buffer.updating) {
                if (verbose) { console.log("Streaming started: ", memview[0], memview[1], memview[2], memview[3], memview[4]); }
                stream_started = true;
                source_buffer.appendBuffer(arr);          
                cc = cc + 1;
            }else{
                queue.push(arr); // add to the end
            }
            if (verbose) { console.log("queue push:", queue.length); }
        }

        function loadPacket() { // called when source_buffer is ready for more
            if (!source_buffer.updating) { // really, really ready
                if (queue.length > 0) {
                    var inp = queue.shift(); // pop from the beginning
                    if (verbose) { console.log("queue pop:", queue.length); }
                    var memview = new Uint8Array(inp);
                    if (verbose) { console.log(" ==> writing buffer with", memview[0], memview[1], memview[2], memview[3]); }
                    source_buffer.appendBuffer(inp);
                    cc = cc + 1;
                }
                // else { // the queue runs empty, so the next packet is fed directly
                //     stream_started = false;
                // }
            }
            // else { 
            //     // so it was not?

            // }
        }

        function opened() { // MediaSource object is ready to go
            // https://developer.mozilla.org/en-US/docs/Web/API/MediaSource/duration
            ms.duration = buffering_sec;
            source_buffer = ms.addSourceBuffer(codecPars);
            // https://developer.mozilla.org/en-US/docs/Web/API/source_buffer/mode
            var myMode = source_buffer.mode;
            source_buffer.mode = 'sequence';
            // source_buffer.mode = 'segments';
            source_buffer.addEventListener("updateend", loadPacket);
            //ws = new WebSocket("ws://49.235.89.102:15555/live.mp4?sim=19019000001&channel=3&token=123456");
            
            ws = new WebSocket("ws://49.235.89.102:15555/live.mp4?sim=1901305037&channel=2&token=123456");
            //ws = new WebSocket("ws://127.0.0.1:81/live/JT1078_8.live.mp4"); //创建WebSocket连接
            ws.binaryType = 'arraybuffer';
            ws.onmessage = function (e) {
                //当客户端收到服务端发来的消息时，触发onmessage事件，参数e.data包含server传递过来的数据
                //console.log(e.data);
                putPacket(e.data);
                //source_buffer.appendBuffer(e.data);
            }
        }

        function startup() {
            ms.addEventListener('sourceopen', opened, false);
            // get reference to video
            stream_live = document.getElementById('stream_live');
            // set mediasource as source of video
            stream_live.src = window.URL.createObjectURL(ms);
        }
        function base64ToArrayBuffer(base64) {
            var binary_string = window.atob(base64);
            var len = binary_string.length;
            var bytes = new Uint8Array(len);
            for (var i = 0; i < len; i++) {
                bytes[i] = binary_string.charCodeAt(i);
            }
            return bytes.buffer;
        }
        window.onload = function () {
            startup();
        }

    </script>
</body>
</html>